const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PENDING = 'pending';

//  写法思路在README.md文件中

class MyPromise {
    constructor(executor) {
        //  执行器，会立即执行，它接受两个参数，一个成功的回调，一个失败的回调
        try {
            executor(this.resolve, this.reject);
        } catch (error) {
            this.reject(error)
        }
    }

    //  设置当前promise的状态值
    status = PENDING;
    //  记录成功时返回的值
    value = undefined;
    //  记录失败时返回的值
    reason = undefined;
    //  记录成功回调
    successCallback = [];
    //  记录失败回调
    failCallback = [];

    resolve = (value) => {
        //  如果当前状态不是初始状态，则不能被修改
        if (this.status !== PENDING) return;
        //  将当前状态修改为成功
        this.status = FULFILLED;
        //  记录成功时返回的值
        this.value = value;
        //  当执行器为异步时，执行器执行完毕，需要执行成功回调
        while (this.successCallback.length) this.successCallback.shift()();
    }

    reject = (reason) => {
        //  如果当前状态不是初始状态，则不能被修改
        if (this.status !== PENDING) return;
        //  将当前状态修改为失败
        this.status = REJECTED;
        //  记录失败时返回的值
        this.reason = reason;
        //  当执行器为异步时，执行器执行完毕，需要执行失败回调
        while (this.failCallback.length) this.failCallback.shift()();
    }

    then(successCallback, failCallback) {
        //  参数变成可选的，当不传递参数时相当于传递了类似的函数
        //  这个地方要加上判断是不是函数类型，如果是普通值则忽略掉
        if (successCallback) {
            if (typeof successCallback !== 'function') {
                successCallback = value => value;
            }
        } else {
            successCallback = value => value;
        }
        if (failCallback) {
            if (typeof failCallback !== 'function') {
                failCallback = reason => { throw reason }
            }
        } else {
            failCallback = reason => { throw reason }
        }
        const promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        const x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        const x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === PENDING) {
                //  当执行器为异步的时候，需要记录回调函数，等待执行器执行完毕后在执行回调函数
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            const x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                });
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            const x = failCallback(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                });
            }
        })
        return promise2;
    }

    finally(callback) {
        return this.then(value => {
            const x = callback();
            return MyPromise.resolve(x).then(() => value)
        }, error => {
            const x = callback();
            return MyPromise.reject(x).then(undefined, () => error)
        })
    }

    catch(callback) {
        this.then(undefined, callback);
    }

    static all(array) {
        const result = [];
        let i = 0;
        return new MyPromise((resolve, reject) => {
            const addData = (key, value) => {
                result[key] = value;
                i++;
                if (i === array.length) {
                    resolve(result);
                }
            }
            array.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    item.then(value => addData(index, value), error => reject(error))
                } else {
                    addData(index, item);
                }
            })
        })
    }

    static resolve(item) {
        if (item instanceof MyPromise) {
            return item;
        } else {
            return new MyPromise(resolve => {
                resolve(item)
            })
        }
    }

    static reject(error) {
        return new MyPromise((resolve, reject) => {
            reject(error)
        })
    }
}

//  链式调用then时，需要看回调返回的值是普通值还是promise的，普通值可以直接向下传递，promise的还需要等待完成才能向下传递
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject);
    } else {
        resolve(x);
    }
}

/*-------------------------------------------------------------------------------------------------------- */

const promise1 = new MyPromise((resolve, reject) => {
    // resolve('同步时，成功回调接收到消息')
    // setTimeout(() => {
    //     resolve('异步时，成功回调延迟接收到消息')
    // }, 2000)
    // reject('同步时，失败回调接受到消息')
    setTimeout(() => {
        reject('异步时，失败回调延迟接收到消息')
    }, 2000)
})
// promise1.then(value => {
//     console.log(value)
// }, error => {
//     console.log(error)
// })

/*-------------------------------------------------------------------------------------------------------- */

//  一个promise可以同时执行多个then方法
const promise2 = new MyPromise((resolve, reject) => {
    // resolve('同步时，我想被多个成功回调执行')
    // setTimeout(() => {
    //     resolve('异步时，我想被多个成功回调执行')
    // }, 2000)
    // reject('同步时，我想被多个失败回调执行')
    setTimeout(() => {
        reject('异步时，我想被多个失败回调执行')
    }, 2000)
})

// promise2.then(value => {
//     console.log('我是第一个执行成功 ' + value);
// }, error => {
//     console.log('我是第一个执行错误 ' + error);
// })

// promise2.then(value => {
//     console.log('我是第二个执行成功 ' + value);
// }, error => {
//     console.log('我是第二个执行错误 ' + error);
// })

// promise2.then(value => {
//     console.log('我是第三个执行成功 ' + value);
// }, error => {
//     console.log('我是第三个执行错误 ' + error);
// })

/*-------------------------------------------------------------------------------------------------------- */

//  promise链式调用
const promise3 = new MyPromise((resolve, reject) => {
    // resolve('同步时，我是第一个then中成功回调显示的值')
    // setTimeout(() => {
    //     resolve('异步时，我是第一个then中成功回调显示的值');
    // }, 2000)
    // reject('同步时，我是第一个then中失败回调显示的值');
    setTimeout(() => {
        reject('异步时，我是第一个then中失败回调显示的值');
    }, 2000)
})

const promise4 = () => {
    return new MyPromise((resolve, reject) => {
        // resolve('同步时，我是第二个then中成功回调显示的值')
        // setTimeout(() => {
        //     resolve('异步时，我是第二个then中成功回调显示的值');
        // }, 2000)
        // reject('同步时，我是第二个then中失败回调显示的值');
        setTimeout(() => {
            reject('异步时，我是第二个then中失败回调显示的值');
        }, 2000)
    })
}

// promise3.then(value => {
//     console.log(value);
//     return promise4();
// }, error => {
//     console.log('第一个失败回调... ', error)
//     return promise4();
// }).then(value => {
//     console.log(value);
// }, error => {
//     console.log('第二个失败回调... ', error)
// })

/*-------------------------------------------------------------------------------------------------------- */

//  then方法中不能返回自己，否则会发生循环调用
const promise5 = new MyPromise((resolve, reject) => {
    resolve('同步时，then方法中返回自己')
    // setTimeout(() => {
    //     resolve('异步时，我是第二个then中成功回调显示的值');
    // }, 2000)
    // reject('同步时，我是第二个then中失败回调显示的值');
    // setTimeout(() => {
    //     reject('异步时，我是第二个then中失败回调显示的值');
    // }, 2000)
})

// const p = promise5.then(value => {
//     console.log(value);
//     return p;
// })

// p.then(value => {
//     console.log(value)
// }, error => {
//     console.log(error)
// })

/*-------------------------------------------------------------------------------------------------------- */

//  错误处理
const promise6 = new MyPromise((resolve, reject) => {
    //throw '执行器错误，被reject抓取到'
    //resolve(100)
    reject(100)
})
// promise6.then(value => {
//     console.log(value)
//     throw 'then方法成功回调错误，被reject抓取到'
// }, error => {
//     console.log(error)
//     throw 'then方法失败回调错误，被reject抓取到'
// }).then(value => {
//     console.log(value)
// }, error => {
//     console.log(error)
// })


/*-------------------------------------------------------------------------------------------------------- */

//  then是可选参数
const promise7 = new MyPromise((resolve, reject) => {
    resolve('then是可选参数，成功回调');
    // reject('then是可选参数,失败回调')
})

// promise7
// .then(2)
// .then(value => {
//     console.log(value)
// }, error => {
//     console.log(error)
// })

/*-------------------------------------------------------------------------------------------------------- */

//  promise.all
const promise8 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise.all方法执行')
    }, 2000)
    // reject('then是可选参数,失败回调')
})

// MyPromise.all([1, 2, promise8, 3])
// .then(value => {
//     console.log(value)
// },error => {
//     console.log(error)
// })

/*-------------------------------------------------------------------------------------------------------- */

//  promise.resolve

const promise9 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise.resolve方法执行')
        // reject('promise.resolve方法执行')
    }, 2000)
})

// MyPromise.resolve(2).then(value => {
//     console.log(value)
// }, error => {
//     console.log(error)
// })

// MyPromise.resolve(promise9).then(value => {
//     console.log(value)
// }, error => {
//     console.log(error)
// })

/*-------------------------------------------------------------------------------------------------------- */

//  promise.reject
// MyPromise.reject(2)
// .then(value => {
//     console.log(value)
// }, error => {
//     console.log(error)
// })

/*-------------------------------------------------------------------------------------------------------- */


//  promise.finally
const promise10 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        reject(100)
        // reject('promise.resolve方法执行')
    }, 2000)
})

// promise10.finally(() => {
//     console.log('promise.finally方法执行')
// }).then(value => {
//      console.log(value)
// }, error => {
//      console.log(error)
// })


/*-------------------------------------------------------------------------------------------------------- */

//  promise.catch
const promise11 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        reject('被catch抓取错误')
        // reject('promise.resolve方法执行')
    }, 2000)
})

// promise11.catch(error => {
//     console.log(error)
// })

/*-------------------------------------------------------------------------------------------------------- */
